
/**
 * date : 2018年2月25日
 * header, footer
 */
var header = '',
	footer = '',
	header_break = '';

var _t = new Date().getTime();
$.get(ctx+'/my/tpl/a102/header.html?t='+_t, function(_data){
	header = _data;
});

$.get(ctx+'/my/tpl/a102/footer.html?t='+_t, function(_data){
	footer = _data;
});

$.get(ctx+'/my/tpl/a102/header_break_before.html?t='+_t, function(_data){
	header_break = _data;
});

/**
 * 参考"my105_testPrintPageHeight.jsp"
 * 
 * 在以上页面可以测试页面的高度
 */
var PAGE_H = 1110;

/**
 * 2018年2月26日
 * 
 * 段落元素的`margin-top:0;margin-bottom:5px`
 */
var MARGIN_EXTRA = 5;

/**
 * 数组,按page划分
 * 每一个元素代表一页(填充到main(header,main,footer))
 */
var block_array = [];

/**
 * ue ifr
 * ifr101
 * ifr102
 * "header","footer"的高度(emr_headerH, emr_footerH)
 * 填充高度: pageMainH
 */
var ifrdoc = null,
	body = null,
	emrifrdoc1 = null,
	emrifrbody1 = null,
	emrifrdoc2 = null,
	emrifrbody2 = null,
	emr_headerH = 0,
	emr_footerH = 0,
	pageMainH = 0;

/**
 * `nonTableSplit`函数会使用此`seedId`变量
 */
var seedId = 1;

/**
 * 2018年2月28日
 * `EventProxy`: An implementation of task/event based asynchronous pattern.
 */
var ep = new EventProxy();

/**
 * date: 2018年2月23日
 * 
 * js循环遍历节点,并将纯文本包装(wrap)
 * 递归遍历元素节点,
 * (1)遇到文本节点,wrap;
 * (2)遇到标签递归,
 * 
 * [ typeof ](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/typeof)
 * console.log( (typeof ele) , $(ele).text() );
 * 
 * update at 2018年2月27日, 优化代码,加入对`<p><br></p>`类型元素的判断
 * 
 */
var LoopInlineNodes = (function () {
	var regexp = new RegExp("(<.+?>).+(<\/.+?>)");
	var noCloseTagArr = ['br','hr'];// 这些元素可以不用闭合: br,hr,img,input,li,p
	var htmlStr = null;
    /**
     * 构造函数
     */
	function LoopInlineNodes() {
        htmlStr = '';
    }
    /**
     * 检查一个元素是否在一个数组中
     */
    function checkInArr(val, arr, ignoreCase){
    	var flag = false;
    	var val = val || '';
    	var arr = arr || [];
    	/**
    	 * var ignoreCase = ignoreCase || true;
    	 * 1) JS陷阱: 上面的代码是不可取的, 如果传入`false`计算的值也是true
    	 * var ignoreCase = (ignoreCase == undefined ? true : false);
    	 * 2) 上面的代码也是不可取的, 如果传入`true`计算的值也是false
    	 * var ignoreCase = (ignoreCase == undefined ? true : ignoreCase);
    	 * 3) 上面的写法才是正确的
    	 */
    	var ignoreCase = (ignoreCase === undefined ? true : ignoreCase);
    	var len = arr.length;
    	for(var i=0;i<len;i++){
    		if(ignoreCase){
    			if( val.toString().toUpperCase() === arr[i].toString().toUpperCase() ){
    				flag = true;
    				break;
    			}
    		}else{
    			if( val.toString() === arr[i].toString() ){
    				flag = true;
    				break;
    			}
    		}
    	}
    	return flag;
    }
    /**
     * 核心代码,递归
     */
    function _loop(ele){
        if (ele.nodeType === 3) {
        	var text = $(ele).text();
            var len = text.length;
            for (var i = 0; i < len; i++) {
                htmlStr += '<span class="emr">' + text[i] + '</span>';
            }
        }
        else {
            var startTag = '';
            var endTag = '';
            var tagName = $(ele).get(0).tagName;

            /**
             * 这里只考虑"br","hr"; img,input,li不用考虑
             */
            if( checkInArr(tagName,['br','hr']) ){
            	startTag = $(ele)[0].outerHTML;
            	endTag = '';
            }else{
            	if (regexp.test($(ele)[0].outerHTML)) {
                    startTag = RegExp.$1;
                    endTag = RegExp.$2;
                }
            }
            
            htmlStr += startTag;
            var contentsArr = $(ele).contents();
            if (contentsArr && contentsArr.length > 0) {
                $.each(contentsArr, function (i, n) {
                	_loop(n);
                });
            }
            htmlStr += endTag;
        }
    }
    LoopInlineNodes.prototype.getHtmlStr = function () {
        return htmlStr;
    };
    LoopInlineNodes.prototype.loop = function (ele) {
    	_loop(ele);
    };
    LoopInlineNodes.prototype.checkInArr = checkInArr;
    return LoopInlineNodes;
}());


/**
 * 检查一个元素是否在一个数组中
 * 
 * @param val
 * @param arr
 * @param ignoreCase
 * @returns {Boolean}
 */
function checkInArr(val, arr, ignoreCase){
	var flag = false;
	var val = val || '';
	var arr = arr || [];
	/**
	 * var ignoreCase = ignoreCase || true;
	 * 1) JS陷阱: 上面的代码是不可取的, 如果传入`false`计算的值也是true
	 * var ignoreCase = (ignoreCase == undefined ? true : false);
	 * 2) 上面的代码也是不可取的, 如果传入`true`计算的值也是false
	 * var ignoreCase = (ignoreCase == undefined ? true : ignoreCase);
	 * 3) 上面的写法才是正确的
	 */
	var ignoreCase = (ignoreCase === undefined ? true : ignoreCase);
	var len = arr.length;
	for(var i=0;i<len;i++){
		if(ignoreCase){
			if( val.toString().toUpperCase() === arr[i].toString().toUpperCase() ){
				flag = true;
				break;
			}
		}else{
			if( val.toString() === arr[i].toString() ){
				flag = true;
				break;
			}
		}
	}
	return flag;
}

/**
 * 将`span.emrbreak`的所有父元素的`StartTag`,`EndTag`聚合
 * 
 * 
 * @param targetEle
 * @returns {"startTagGather":'', "endTagGather":''};
 */
function getStartAndEndTagGather(targetEle){
	/**
	 * 匹配"StartTag"和"EndTag"的正则
	 */
	var regexp = new RegExp("(<.+?>).+(<\/.+?>)");
	
	var emrbreak = targetEle.find('span.emrbreak').eq(0);
	var allNecessaryParents = [];
	/**
	 * API: http://api.jquery.com/parents/
	 * 
	 * `parents()`会将'body','html'都拿出来
	 */
	$.each(emrbreak.parents(), function(i,n){
		if(!checkInArr($(n).get(0).tagName, ['body','html'])){
			allNecessaryParents.push( n );
		}
	});
	var len = allNecessaryParents.length;
	var startTagGather = '', endTagGather = '';
	var endTagArr = [];
	for(var i=len-1; i>=0; i--){
		//console.log( allNecessaryParents[i] );
    	if (regexp.test($(allNecessaryParents[i])[0].outerHTML)) {
    		startTagGather += RegExp.$1;
    		endTagArr.push(RegExp.$2);
        }
	}
	var len2 = endTagArr.length;
	for(var j=len2-1; j>=0; j--){
		endTagGather += endTagArr[j];
	}
	return {"startTagGather":startTagGather, "endTagGather":endTagGather};
}



$(function(){
	/**
	 * date: 2018年2月25日
	 * 
	 */
	$('#btnSplitAllEles').on('click', function(){
		
		var ue = null;
		var emriframe1 = null;
		var emriframe2 = null;
		
		block_array = []; // 每次点击都要重新初始化!
		
		/**
		 * 1) 变量的初始化
		 */
		var variableReady = function(){
			ue = UE.getEditor('editor');
			ifrdoc = ue.iframe.contentWindow.document;
			body = $('body', ifrdoc);
			
			emriframe1 = $('#emriframe101');
			emrifrdoc1 = emriframe1[0].contentWindow.document;
			emrifrbody1 = $('body', emrifrdoc1);
			
			emriframe2 = $('#emriframe102');
			emrifrdoc2 = emriframe2[0].contentWindow.document;
			emrifrbody2 = $('body', emrifrdoc2);
			
			/**
			 * important!: 
			 * 给iframe的body元素设置width样式, 使隐藏ifr与UE的宽度样式一致,以保证高度的测量是准确的
			 */
			emrifrbody1.css('width', body.width());
			emrifrbody2.css('width', body.width());
			
			setTimeout(function(){
				ep.emit('variableReady', 'Variable Ready ...');
			}, 10);
		};
		
		/**
		 * 2) 获得`pageMainH`
		 */
		var obtainPageMainHeight = function(){
			emrifrbody1.empty();
			emrifrbody1.append( header );
			emrifrbody1.append( footer.replace('{{num}}', 1) );
			
			/**
			 * 取得"header","footer"的高度
			 */
			emr_headerH = emrifrbody1.find('div.emr_header').eq(0).outerHeight(true);
			emr_footerH = emrifrbody1.find('div.emr_footer').eq(0).outerHeight(true);
			
			/**
			 * 填充高度: pageMainH
			 * 用打印纸的高度减去(header和footer的高度之和),额外减去`5`像素的高度,作为当前页面的填充高度
			 */
			pageMainH = PAGE_H - (emr_headerH + emr_footerH) - 5;
		};
		
		/**
		 * 3) 页面装载: 将UE的内容载入"emrifrbody2"中
		 */
		var pageLoad = function(){
			emrifrbody2.empty();
			emrifrbody2.append( body.get(0).innerHTML );
			
			setTimeout(function(){
				ep.emit('pageLoad', '');
			}, 10);
		};
		
		/**
		 * 4) 将拆分后的html载入ifr,准备打印
		 */
		var loadBlockArray = function(){
			emrifrbody2.empty();
			var finalHtmlStr = '';
			$.each(block_array, function(i,n){
				//TODO: page-break-before:always;
				if(i==0){
					finalHtmlStr += header;
				}else{
					finalHtmlStr += header_break;
				}
				finalHtmlStr += '<div style="height:'+(pageMainH+3)+'px;margin:0;padding:0;overflow:hidden;">';
				$.each(n, function(ia,na){
					finalHtmlStr += na;
				});
				finalHtmlStr += '</div>';
				finalHtmlStr += footer.replace('{{num}}', (i+1));
			});
			emrifrbody2.append( finalHtmlStr );
			setTimeout(function(){
				ep.emit('loadBlockArray', 'loadBlockArray ready...');
			}, 100);
		};
		
		
		/**
		 * 注册"EventProxy"(all)事件, 后面的逻辑是依赖前面的逻辑的, 所以代码是嵌套的!
		 */
		ep.all('variableReady', function(variableReady){
			
			// (2) 获得`pageMainH`
			obtainPageMainHeight();
			
			/**
			 * 页面装载: 将UE的内容载入"emrifrbody2"中
			 */
			ep.all('pageLoad', function(pageLoad){
				
				ep.all('loopFindBlockBreak', function(loopFindBlockBreak){
					
					ep.all('loadBlockArray', function(loadBlockArray){
						$('#emriframe102')[0].contentWindow.print();
					});

					// (4) 将拆分后的html载入ifr,准备打印
					loadBlockArray();
					
				});
				// 递归调用(最重要的代码逻辑都在这里)
				loopFindBlockBreak();
			});
			
			// (3) 页面装载: 将UE的内容载入"emrifrbody2"中
			pageLoad();
		});
		
		// (1) 变量的初始化
		variableReady();
	});
});




/**
 * date: 2018年2月26日
 * 
 * 递归调用
 * 
 */
function loopFindBlockBreak(){
	
	var _outerH = 0, prevN = null;
	var blockBreakN = null; //超出"填充高度"的那个元素(`BreakN`)
	var blockList = []; // 没有超出"填充高度"的元素的集合
	var outerHtmlArr = []; // `outerHTML`的集合
	
	var prevH = 0; // `BreakN`的起始高度(与`position.top`(breakElePosition.top)有细微的差别)
	var breakElePosition = null; // `BreakN`的`position`
	/**
	 * 遍历子元素,找到超出"填充高度"的那个元素
	 * 将没有超出"填充高度"的元素收集起来
	 */
	$.each( emrifrbody2.children(), function(i,n){
		_outerH += $(n).outerHeight(true);
		if( _outerH > pageMainH ){
			$(n).attr('id', 'emrbreak_'+(seedId++)); //设置id
			blockBreakN = n;
			breakElePosition = $(n).position();
			return false;
		}
		blockList.push( n );
		prevN = n;
		prevH = _outerH;
	} );
	
	/**
	 * 将没有超出"填充高度"的元素的`outerHTML`取出之后,将其从doc中移除
	 * $(n).remove(); // important!!
	 */
	$.each(blockList, function(i,n){
		outerHtmlArr.push( $(n).get(0).outerHTML );
		$(n).remove(); // important!!
	});
	/**
	 * `outerHtmlArr`是个数组, 存储每个一级子元素的`outerHTML`
	 */
	block_array.push( outerHtmlArr );
	
	if(blockBreakN){
		var blockTagName = $(blockBreakN)[0].tagName;
		
		if('table'.toUpperCase() === blockTagName.toUpperCase()){
			
			tableSplit( blockBreakN, prevH );
			
		}else{
			
			nonTableSplit( blockBreakN, prevH );
			
		}
	}else{
		ep.emit('loopFindBlockBreak', 'loopFindBlockBreak done...')
	}
	
}

/**
 * date: 2018年2月27日
 * 
 * 处理非table元素的拆分！
 * 
 * @param blockBreakN 需要被拆分的元素(非table)
 * @param prevH `blockBreakN`在页面中的起始高度
 * @returns 
 * 
 */
function nonTableSplit(blockBreakN, prevH){
	
	var blockBreakNouterHTML = null;
	var blockBreakN_id = null;
	
	var paragraph = null;
	var isSingleLine = false;
	
	var lin = null; // LoopInlineNodes
	var _newid = null;
	var emrArray = null;
	var len = null;
	var firstPos = null;
	var lastPos = null;
	
	/**
	 * 在ifr101中处理
	 */
	emrifrbody1.empty();
	blockBreakNouterHTML = $(blockBreakN).get(0).outerHTML;
	
	blockBreakN_id = $(blockBreakN).attr('id');
	
	emrifrbody1.append( blockBreakNouterHTML );
	
	/**
	 * 不管是p元素还是其他的元素统一用"paragraph"来定义
	 */
	paragraph = emrifrbody1.children().eq(0);
	
	/**
	 * `LoopInlineNodes`将所有字符添加`span.emr`标签
	 */
	lin = new LoopInlineNodes();
	lin.loop( paragraph.get(0) );
	_newid = paragraph.attr('id')+'_0'+(seedId++);
	paragraph.attr('id', _newid);
	$('#'+_newid, emrifrdoc1).before( lin.getHtmlStr() );
	$('#'+_newid, emrifrdoc1).remove();
	emrArray = emrifrbody1.find('span.emr');
	/**
	 * 新情况: <p><br></p>
	 * body.find('span.emr').length == 0
	 */
	if(emrArray.length == 0){
		// 此情况下,不用拆分. 也不用额外处理! `blockBreakN`随其他元素在下一次递归中被处理 
	}else{
		len = emrArray.length;
		
		var firstCharacter = $(emrArray[0]);
		firstPos = firstCharacter.position();
		var firstCharacterH = firstCharacter.outerHeight(true);
		lastPos = $(emrArray[len-1]).position();
		
		if(firstPos.top === lastPos.top){
			isSingleLine = true;
		}else{
			isSingleLine = false;
		}
		if(isSingleLine){
			// 此情况下,也不用拆分了,因为只有一行. 
		}else{
			var currentTop = firstPos.top; // (当前)`position.top`, y坐标
			var currentLineH = firstCharacterH; // (当前)行高
			if( prevH+currentLineH > pageMainH ){
				// 此情况下,也不用拆分了,因为第一行的高度大于剩余的高度.
			}else{
				$.each(emrArray, function(i,n){
					if( $(n).position().top > currentTop ){ // 这是换行后的第一个元素
						currentTop = $(n).position().top;
						currentLineH += $(n).outerHeight(true);
						if( prevH+currentLineH > pageMainH ){ // 找到拆分的位置点,插入自定义标记
							$(n).before('<span class="emrbreak"></span>');
							return false; // 退出`each`循环
						}
					}
				});
				//TODO: 找出'span.emrbreak'的所有父元素,将它们的startTag,endTag获取到,用正则表达式将它们分开! 应该先做测试
				// emrifrbody1.find('span.emrbreak').eq(0);
				var targetEle = emrifrbody1.children().eq(0);
				var startAndEndTagGather = getStartAndEndTagGather( targetEle );
				
				var firstPartReg = new RegExp('(.*)(?=<span class="emrbreak"></span>)');
				//TODO: `?<=` 在旧版本的浏览器上不支持 (https://stackoverflow.com/questions/5952936/invalid-regular-expression-error)
				// Positive lookbehinds (your ?<=) are not supported in JavaScript, which is causing your RegEx to fail.
				var secondPartReg = new RegExp('(?<=<span class="emrbreak"></span>)(.*)');
				var firstPart = '', secondPart = '';
				if( firstPartReg.test(targetEle.get(0).outerHTML) ){
					firstPart = RegExp.$1;
				}
				if( secondPartReg.test(targetEle.get(0).outerHTML) ){
					secondPart = RegExp.$1;
				}
				firstPart = firstPart + startAndEndTagGather['endTagGather'];
				secondPart = startAndEndTagGather['startTagGather'] + secondPart;
				
				/**
				 * 移除`id`避免重复
				 */
				secondPart = secondPart.replace(/id=".+?"/, '');
				
				/**
				 * 将"上半部分"放入到上一页的数组中
				 */
				block_array[block_array.length-1].push(firstPart);
				/**
				 * 使用"下半部分"替换之前的标签内容
				 * important: 这时要使用`emrifrdoc2`, `emrifrbody2`
				 * //emrifrbody2.find(blockBreakNouterHTML);
				 * 
				 * important!: update at 2018年2月28日: 使用`id`能够准确的获取元素,并能够顺利的`remove()`
				 */
				var targetBreakEle = $('#'+blockBreakN_id, emrifrdoc2);
				targetBreakEle.before( secondPart );
				targetBreakEle.remove();
				
			}
		}
		
	}
	
	loopFindBlockBreak();
	
}

/**
 * 对`table`元素的拆分
 * 
 * @param blockBreakN
 * @param prevH
 */
function tableSplit(blockBreakN, prevH){
	
	var blockBreakNouterHTML = null;
	var blockBreakN_id = null;
	
	var table = null;
	var howManyTrs = null;
	
	var _newid = null;
	var emrArray = null;
	var len = null;
	var firstPos = null;
	var lastPos = null;
	
	/**
	 * 在ifr101中处理
	 */
	emrifrbody1.empty();
	blockBreakNouterHTML = $(blockBreakN).get(0).outerHTML;
	
	blockBreakN_id = $(blockBreakN).attr('id');
	
	emrifrbody1.append( blockBreakNouterHTML );
	
	table = emrifrbody1.children().eq(0);
	
	howManyTrs = table.find('tr').length;
	
	if(howManyTrs == 1){
		// 如果只有一个tr,就不用再拆分啦
	}else{
		var regexp = new RegExp("(<.+?>).+(<\/.+?>)");
		var startTag = '', endTag = '';
		if(regexp.test(blockBreakNouterHTML)){
			startTag = RegExp.$1;
			endTag = RegExp.$2;
		}
		
		var currentH = 0;
		var breakIndex = 0;
		var firstTableStr = startTag, 
			secondTableStr = startTag;
		$.each($(table).find('tr'), function(i,n){
			currentH += $(n).outerHeight(true);
			if(prevH+currentH > pageMainH){
				breakIndex = i;
				return false;
			}
		});
		$.each($(table).find('tr'), function(i,n){
			if(i < breakIndex){
				firstTableStr += $(n).get(0).outerHTML;
			}else{
				secondTableStr += $(n).get(0).outerHTML;
			}
		});
		
		firstTableStr += endTag;
		secondTableStr += endTag;
		block_array[block_array.length-1].push(firstTableStr);
		
		var targetEle = $('#'+blockBreakN_id, emrifrdoc2);
		targetEle.before(secondTableStr);
		targetEle.remove();
	}
	
	loopFindBlockBreak();
	
}

